// Include standard headers
#include <stdio.h>
#include <stdlib.h>
//#include <iostream>

// Include GLEW
#include <GL/glew.h>
#include <opencv2/opencv.hpp>
// Include GLFW
#include <GLFW/glfw3.h>
#include <Eigen/Eigen>
// Include GLM
#include <glm/glm.hpp>
#include "dragon.h"
#include <glm/gtc/matrix_transform.hpp>
#include "readpng.h"
using namespace glm;

#include <common/shader.hpp>
#include <common/texture.hpp>
#include <common/controls.hpp>
#include <common/objloader.hpp>
#include <common/vboindexer.hpp>
#include <common/text2D.hpp>

#include <common/model.hpp>
#include <fstream>
#include <vector>

GLFWwindow* window;
void InitText(std::vector<std::string> picname, GLuint cubetext[6])
{
    glEnable(GL_TEXTURE_2D);
    for(int i=0;i<6;i++){
        std::string curname = picname[i];
        cubetext[i] = loadPNGTexture(curname.c_str());
        glBindTexture(GL_TEXTURE_2D, cubetext[i]);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    }
    glDisable(GL_TEXTURE_2D);
}

void error_callback(int error, const char* description)
{
    puts(description);
}
inline void swap(int &a, int &b)
{ int t = a; a = b; b = t; }
inline void swap(float &a, float &b)
{
    float t = a;
    a = b;
    b = t;
}

int partition(cv::Mat& m, int *a, int p, int r) {      //对 a[p, r) 原址重排
    float x = m.at<float>(0, r-1);                        //a[r] ==> a[r-1]
    int i = p - 1;
    for (int j = p; j < r - 1; ++j)
        if (m.at<float>(0, j) >= x) {
            ++i;
            swap(a[i], a[j]);
            swap(m.at<float>(0, i), m.at<float>(0, j));
        }
    swap(a[i+1], a[r-1]);
    swap(m.at<float>(0, i+1), m.at<float>(0, r-1));
    return i + 1;
}
void quicksort(cv::Mat& m, int *a, int p, int r) {     //调用quicksort(a, 0, n),不是(a, 0, n-1)，区间a[0, n)
    if (p < r - 1) {                       //(p < r) ==>  (p < r - 1)
        int q = partition(m, a, p, r);
        quicksort(m, a, p, q);                //(a, p, q-1)  ==> (a, p, q)
        quicksort(m, a, q+1, r);
    }
}
std::vector<Eigen::Triplet<float> > triple[3];
void Wave(cv::Mat& img)
{
     int Height = img.cols;
     int Width = img.rows;
     int depth = 3; //定义分解深度
     int depthcount = 1;
     cv::Mat tmp = cv::Mat::ones(Width, Height, CV_32FC1);
     cv::Mat wavelet = cv::Mat::ones(Width, Height, CV_32FC1);
     cv::Mat imgtmp = img.clone();
     imgtmp.convertTo(imgtmp, CV_32FC1);
     while (depthcount<=depth){
         Width = img.rows / depthcount;
         Height = img.cols / depthcount;
         for (int i = 0; i < Width; i++){
             for (int j = 0; j < Height / 2; j++){
                 tmp.at<float>(i, j) = (imgtmp.at<float>(i, 2 * j) + imgtmp.at<float>(i, 2 * j + 1)) / 2;
                 tmp.at<float>(i, j + Height / 2) = (imgtmp.at<float>(i, 2 * j) - imgtmp.at<float>(i, 2 * j + 1)) / 2;
             }
         }
         for (int i = 0; i < Width / 2; i++){
             for (int j = 0; j < Height; j++){
                 wavelet.at<float>(i, j) = (tmp.at<float>(2 * i, j) + tmp.at<float>(2 * i + 1, j)) / 2;
                 wavelet.at<float>(i + Width / 2, j) = (tmp.at<float>(2 * i, j) - tmp.at<float>(2 * i + 1, j)) / 2;
             }
         }
         imgtmp = wavelet;
         depthcount++;
     }
    // wavelet.convertTo(wavelet, CV_8UC1);
    // wavelet += 50; //图像暗度过低，所以这里我加了50
    // imshow("jpg", wavelet);
     wavelet.copyTo(img);
     wavelet.reshape(0,1).copyTo(wavelet);
     wavelet = wavelet/255;
     wavelet.copyTo(img);
     /*cv::Mat cur;
     wavelet.copyTo(cur);
     quicksort(cur, index, 0, 32*32);
     for(int i=0;i<128;i++){
         std::cout<<index[i]<<":"<<wavelet.at<float>(0, index[i])<<std::endl;
         triple[rgb].push_back(Eigen::Triplet<float>(0, hhh*32*32+index[i], wavelet.at<float>(0, index[i])));
         if(hhh*32*32<0){
             std::cout<<"ggg"<<std::endl;
         }
     }*/
}
void mysort(cv::Mat& m, int rgb)
{
    int index[6*32*32];
    for(int i=0; i<6*32*32;i++){
        index[i] = i;
    }
    cv::Mat cur;
    m.copyTo(cur);
    quicksort(cur, index, 0, 6*32*32);
    for(int i=0;i<128;i++){
        std::cout<<index[i]<<":"<<m.at<float>(0, index[i])<<std::endl;
        triple[rgb].push_back(Eigen::Triplet<float>(0, index[i], m.at<float>(0, index[i])));
    }
}
int DrawTexCube(GLfloat width, GLfloat height, GLfloat depth, GLuint *cubetext)									//
{

    GLfloat x = width / 2, y = height / 2, z = depth / 2;
    glPushMatrix();
    // Front Face
    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, cubetext[0]);
    glBegin(GL_QUADS);
    glTexCoord2f(0.0f, 0.0f);   glVertex3f(-x, -y, z);//Front
    glTexCoord2f(x, 0.0f);   glVertex3f(x, -y, z);
    glTexCoord2f(x, y);	glVertex3f(x, y, z);
    glTexCoord2f(0.0f, y);	glVertex3f(-x, y, z);
    glEnd();
    // Back Face
    glBindTexture(GL_TEXTURE_2D, cubetext[1]);
    glBegin(GL_QUADS);
    glTexCoord2f(0.0f, 0.0f);  glVertex3f(-x, -y, -z);
    glTexCoord2f(0.0f, y);  glVertex3f(-x, y, -z);
    glTexCoord2f(x, y);  glVertex3f(x, y, -z);
    glTexCoord2f(x, 0.0f);  glVertex3f(x, -y, -z);
    glEnd();
    // Top Face
    glBindTexture(GL_TEXTURE_2D, cubetext[2]);
    glBegin(GL_QUADS);
    glTexCoord2f(0.0f, 0.0f); glVertex3f(-x, y, -z);
    glTexCoord2f(0.0f, z); glVertex3f(-x, y, z);
    glTexCoord2f(x, z); glVertex3f(x, y, z);
    glTexCoord2f(x, 0.0f); glVertex3f(x, y, -z);
    glEnd();
    // Bottom Face
    glBindTexture(GL_TEXTURE_2D, cubetext[3]);
    glBegin(GL_QUADS);
    glTexCoord2f(0.0f, 0.0f); glVertex3f(-x, -y, -z);
    glTexCoord2f(x, 0.0f); glVertex3f(x, -y, -z);
    glTexCoord2f(x, z); glVertex3f(x, -y, z);
    glTexCoord2f(0.0f, z); glVertex3f(-x, -y, z);
    glEnd();
    // Right face
    glBindTexture(GL_TEXTURE_2D, cubetext[4]);
    glBegin(GL_QUADS);
    glTexCoord2f(0.0f, 0.0f); glVertex3f(x, -y, -z);
    glTexCoord2f(y, 0.0f); glVertex3f(x, y, -z);
    glTexCoord2f(y, z); glVertex3f(x, y, z);
    glTexCoord2f(0.0f, z); glVertex3f(x, -y, z);
    glEnd();
    // Left Face
    glBegin(GL_QUADS);
    glBindTexture(GL_TEXTURE_2D, cubetext[5]);
    glTexCoord2f(0.0f, 0.0f); glVertex3f(-x, -y, -z);
    glTexCoord2f(0.0f, z); glVertex3f(-x, -y, z);
    glTexCoord2f(x, z); glVertex3f(-x, y, z);
    glTexCoord2f(x, 0.0f); glVertex3f(-x, y, -z);
    glEnd();
    glDisable(GL_TEXTURE_2D);
    glPopMatrix();
    return true;
}
const float PI = 3.1415;
int main(void)
{	
    std::vector<std::string> picname;
    picname.push_back("posz.png");
    picname.push_back("negz.png");
    picname.push_back("posx.png");
    picname.push_back("negx.png");
    picname.push_back("posy.png");
    picname.push_back("negy.png");
    cv::Mat red(1, 6*32*32, CV_32FC1), green(1, 6*32*32, CV_32FC1), blue(1, 6*32*32, CV_32FC1);
    for(int i=0;i<picname.size();i++){
        cv::Mat curpic;
        if (i!=2)
            curpic = cv::imread(picname[i].c_str());
        else
            curpic = cv::Mat::ones(480, 480, CV_8UC3)*255;
        cv::imshow("fuck", curpic);
        cv::waitKey();
        cv::resize(curpic, curpic, cv::Size(32,32));
        curpic.convertTo(curpic, CV_32FC3);
        std::vector<cv::Mat> curpic_rgb;
        cv::split(curpic, curpic_rgb);
        Wave(curpic_rgb[0]);
        Wave(curpic_rgb[1]);
        Wave(curpic_rgb[2]);
        curpic_rgb[0].copyTo(blue(cv::Rect(i*32*32, 0, 32*32,1)));
        curpic_rgb[1].copyTo(green(cv::Rect(i*32*32, 0, 32*32,1)));
        curpic_rgb[2].copyTo(red(cv::Rect(i*32*32, 0, 32*32,1)));
    }
    mysort(red, 0);
    mysort(green, 1);
    mysort(blue, 2);
    Eigen::SparseMatrix<float, Eigen::RowMajor> Lr(1,6*32*32),
            Lg(1,6*32*32), Lb(1, 6*32*32);
   // std::vector<Eigen::Triplet<float> > cur = triple[0];
    Lr.setFromTriplets(triple[0].begin(), triple[0].end());
    Lg.setFromTriplets(triple[1].begin(), triple[1].end());
    Lb.setFromTriplets(triple[2].begin(), triple[2].end());
    std::ifstream in_t("cvt.txt",std::ios::binary);
    Eigen::MatrixXf T(6*32*32, 50000);
    std::ifstream in("T_hhh.txt", std::ios::binary);
    in.read((char*)T.data(), T.rows()*T.cols()*sizeof(float));
   /* memset(T.data(), 0, T.rows()*T.cols()*sizeof(float));
    float value;
    int row, col;
    while(in_t.read((char*)&value, sizeof(float))
          && in_t.read((char*)&row, sizeof(int))
          &&in_t.read((char*)&col, sizeof(int))
          ){
        T(col, row) = value/255;
    }
    std::ofstream out("T_hhh.txt", std::ios::binary);
    out.write((char*)T.data(), T.rows()*T.cols()*sizeof(float));*/
    std::cout<<T.col(0)<<std::endl;
    Eigen::SparseMatrix<float, Eigen::RowMajor>::InnerIterator it(Lr, 0);
    while(it){
        std::cout<<it.value()<<" ";
        ++it;
    }
    std::cout<<std::endl;
    // Initialise GLFW
	if (!glfwInit())
	{
		fprintf(stderr, "Failed to initialize GLFW\n");
		return -1;
	}

	glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
	glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1);

	// Open a window and create its OpenGL context
	window = glfwCreateWindow( 1024, 768, "Tutorial 01", NULL, NULL);
	if (window==NULL)
	{
        fprintf(stderr, "Failed to open GLFW window. If you have an Intel GPU, they are not 3.3 compatible. Try the 2.1 version of the tutorials.\n");
		glfwTerminate();
		return -1;
	}
	glfwMakeContextCurrent(window);

	// Initialize GLEW
	glewExperimental = true; // Needed for core profile
	if (glewInit() != GLEW_OK) {
		fprintf(stderr, "Failed to initialize GLEW\n");
		return -1;
	}

	glfwSetWindowTitle(window,"Tutorial 01");

	// Ensure we can capture the escape key being pressed below
	glfwSetInputMode(window, GLFW_STICKY_KEYS, GL_TRUE);

	// Dark blue background
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);

	// Enable depth test
	glEnable(GL_DEPTH_TEST);
	// Accept fragment if it closer to the camera than the former one
	glDepthFunc(GL_LESS);

	GLuint VertexArrayID;
	glGenVertexArrays(1, &VertexArrayID);
	glBindVertexArray(VertexArrayID);

	// Create and compile our GLSL program from the shaders
    GLuint programID = LoadShaders("vertexshader.sh",
                            "fragmentshader.sh");

	// Get a handle for our "MVP" uniform
	GLuint MatrixID = glGetUniformLocation(programID, "MVP");

	// Projection matrix : 45?Field of View, 4:3 ratio, display range : 0.1 unit <-> 100 units
    glm::mat4 Projection = glm::perspective(90.0f, 4.0f / 3.0f, 0.1f, 1000.0f);
	// Camera matrix
	glm::mat4 View = glm::lookAt(
        glm::vec3(0, 0, 10), // Camera is at (4,3,-3), in World Space
        glm::vec3(0, 0, 0), // and looks at the origin
		glm::vec3(0, 1, 0)  // Head is up (set to 0,-1,0 to look upside-down)
		);
	// Model matrix : an identity matrix (model will be at the origin)
    glm::mat4 Model = glm::mat4(0.7f);
	// Our ModelViewProjection : multiplication of our 3 matrices
	glm::mat4 MVP = Projection * View * Model; // Remember, matrix multiplication is the other way around

	// Our vertices. Tree consecutive floats give a 3D vertex; Three consecutive vertices give a triangle.
	// A cube has 6 faces with 2 triangles each, so this makes 6*2=12 triangles, and 12*3 vertices
	static const GLfloat g_vertex_buffer_data[] = {
		-1.0f, -1.0f, -1.0f,
		-1.0f, -1.0f, 1.0f,
		-1.0f, 1.0f, 1.0f,
		1.0f, 1.0f, -1.0f,
		-1.0f, -1.0f, -1.0f,
		-1.0f, 1.0f, -1.0f,
		1.0f, -1.0f, 1.0f,
		-1.0f, -1.0f, -1.0f,
		1.0f, -1.0f, -1.0f,
		1.0f, 1.0f, -1.0f,
		1.0f, -1.0f, -1.0f,
		-1.0f, -1.0f, -1.0f,
		-1.0f, -1.0f, -1.0f,
		-1.0f, 1.0f, 1.0f,
		-1.0f, 1.0f, -1.0f,
		1.0f, -1.0f, 1.0f,
		-1.0f, -1.0f, 1.0f,
		-1.0f, -1.0f, -1.0f,
		-1.0f, 1.0f, 1.0f,
		-1.0f, -1.0f, 1.0f,
		1.0f, -1.0f, 1.0f,
		1.0f, 1.0f, 1.0f,
		1.0f, -1.0f, -1.0f,
		1.0f, 1.0f, -1.0f,
		1.0f, -1.0f, -1.0f,
		1.0f, 1.0f, 1.0f,
		1.0f, -1.0f, 1.0f,
		1.0f, 1.0f, 1.0f,
		1.0f, 1.0f, -1.0f,
		-1.0f, 1.0f, -1.0f,
		1.0f, 1.0f, 1.0f,
		-1.0f, 1.0f, -1.0f,
		-1.0f, 1.0f, 1.0f,
		1.0f, 1.0f, 1.0f,
		-1.0f, 1.0f, 1.0f,
		1.0f, -1.0f, 1.0f
	};

	// One color for each vertex. They were generated randomly.
	static const GLfloat g_color_buffer_data[] = {
		0.583f, 0.771f, 0.014f,
		0.609f, 0.115f, 0.436f,
		0.327f, 0.483f, 0.844f,
		0.822f, 0.569f, 0.201f,
		0.435f, 0.602f, 0.223f,
		0.310f, 0.747f, 0.185f,
		0.597f, 0.770f, 0.761f,
		0.559f, 0.436f, 0.730f,
		0.359f, 0.583f, 0.152f,
		0.483f, 0.596f, 0.789f,
		0.559f, 0.861f, 0.639f,
		0.195f, 0.548f, 0.859f,
		0.014f, 0.184f, 0.576f,
		0.771f, 0.328f, 0.970f,
		0.406f, 0.615f, 0.116f,
		0.676f, 0.977f, 0.133f,
		0.971f, 0.572f, 0.833f,
		0.140f, 0.616f, 0.489f,
		0.997f, 0.513f, 0.064f,
		0.945f, 0.719f, 0.592f,
		0.543f, 0.021f, 0.978f,
		0.279f, 0.317f, 0.505f,
		0.167f, 0.620f, 0.077f,
		0.347f, 0.857f, 0.137f,
		0.055f, 0.953f, 0.042f,
		0.714f, 0.505f, 0.345f,
		0.783f, 0.290f, 0.734f,
		0.722f, 0.645f, 0.174f,
		0.302f, 0.455f, 0.848f,
		0.225f, 0.587f, 0.040f,
		0.517f, 0.713f, 0.338f,
		0.053f, 0.959f, 0.120f,
		0.393f, 0.621f, 0.362f,
		0.673f, 0.211f, 0.457f,
		0.820f, 0.883f, 0.371f,
		0.982f, 0.099f, 0.879f
	};

	Mesh cube_mesh;
	cube_mesh.setAtrribute(Pos,(void *) g_vertex_buffer_data, sizeof(g_vertex_buffer_data));
	cube_mesh.setAtrribute(Color, (void *)g_color_buffer_data, sizeof(g_color_buffer_data));
	cube_mesh.vertex_size = sizeof(g_color_buffer_data)/sizeof(float)/3;
	cube_mesh.face_size = cube_mesh.vertex_size / 3;

    Mesh ninja_head("models/ninjaHead_Low_smooth_po.obj");
    std::vector<glm::vec3> colors(ninja_head.vertex_size);
    int face_id = 0;
    float max = 0;
    for (auto &itr : colors)
    {
        int id = faces[face_id/3][face_id%3]-1;
        cv::Mat view(1,3, CV_32FC1);
        view.at<float>(0,0) = 0;
        view.at<float>(0,1) = 0;
        view.at<float>(0,2) = 16;
        Eigen::MatrixXf r, g, b;
        cv::Mat normal(1,3,CV_32FC1, normals[id]);
        cv::normalize(normal, normal);
        cv::Mat curpoint(1, 3, CV_32FC1);
        view = view - curpoint;
        cv::normalize(view, view);
        cv::transpose(view, view);
        cv::Mat cur = normal*view;
        float sin_theta_i = cur.at<float>(0,0);
        float cos_theta_i = sqrt(1 - sin_theta_i*sin_theta_i);
        float cos_theta_t = sqrt(1-sin_theta_i*sin_theta_i/1.3/1.3);
        float t = 2*cos_theta_i/(cos_theta_i+1.3*cos_theta_t);
        float fresnel = 1.3*cos_theta_t/cos_theta_i*(t*t);
        r = fresnel * Lr * T.col(id);
        g = fresnel * Lg * T.col(id);
        b = fresnel* Lb * T.col(id);
        itr.r = r(0,0) / PI;
        itr.g = g(0,0) / PI;
        itr.b = b(0,0) / PI;
        if(itr.r>max){
            max = itr.r;
        }else if(itr.g > max){
            max = itr.g;
        }else if(itr.b > max){
            max = itr.b;
        }
       /* if(itr.r>1 ||
                itr.g >1||
                itr.b>1){
            std::cout<<itr.r<<":"<<itr.g<<":"<<itr.b<<std::endl;
        }*/
        if(itr.r<0 ||
                itr.g <0||
                itr.b<0)
        {
            std::cout<<itr.r<<":"<<itr.g<<":"<<itr.b<<std::endl;
        }
        face_id++;

        //itr.r = (float)rand()/RAND_MAX;
        //itr.g = (float)rand() / RAND_MAX;
        //itr.b = (float)rand() / RAND_MAX;
    }
    for (auto &itr : colors)
    {
        itr.r /= max;
        itr.g /= max;
        itr.b /= max;
        itr.r = 1-itr.r;
        itr.g = 1-itr.g;
        itr.b =1-itr.b;
       /* if(itr.r>1 ||
                itr.g >1||
                itr.b>1){
            std::cout<<itr.r<<":"<<itr.g<<":"<<itr.b<<std::endl;
        }*/


        //itr.r = (float)rand()/RAND_MAX;
        //itr.g = (float)rand() / RAND_MAX;
        //itr.b = (float)rand() / RAND_MAX;
    }
    ninja_head.setAtrribute(Color, (void *)&colors[0], colors.size() * sizeof(glm::vec3));

    auto f = [&ninja_head,&cube_mesh](Mesh * & cur_mesh)
    {
        if (glfwGetKey(window,GLFW_KEY_N) == GLFW_PRESS)
            cur_mesh = &ninja_head;
        else if (glfwGetKey(window,GLFW_KEY_C) == GLFW_PRESS)
        {
            cur_mesh = &cube_mesh;
        }
    };

	//Mesh * cur_mesh = &ninja_head;
    Mesh * cur_mesh = &ninja_head;
    GLuint cubetext[6];
    //InitText(picname, cubetext);
    do{
       // ninja_head.setAtrribute(Color, (void *)&colors[0], colors.size() * sizeof(glm::vec3));
		f(cur_mesh);
		// Clear the screen
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

		// Use our shader
		glUseProgram(programID);

		// Send our transformation to the currently bound shader, 
		// in the "MVP" uniform
		glUniformMatrix4fv(MatrixID, 1, GL_FALSE, &MVP[0][0]);

		// 1rst attribute buffer : vertices
		glEnableVertexAttribArray(0);
		glBindBuffer(GL_ARRAY_BUFFER, cur_mesh->pos_id);
		glVertexAttribPointer(
			0,                  // attribute. No particular reason for 0, but must match the layout in the shader.
			3,                  // size
			GL_FLOAT,           // type
			GL_FALSE,           // normalized?
			0,                  // stride
			(void*)0            // array buffer offset
			);

		// 2nd attribute buffer : colors
		glEnableVertexAttribArray(1);
		glBindBuffer(GL_ARRAY_BUFFER, cur_mesh->color_id);
		glVertexAttribPointer(
			1,                                // attribute. No particular reason for 1, but must match the layout in the shader.
			3,                                // size
			GL_FLOAT,                         // type
			GL_FALSE,                         // normalized?
			0,                                // stride
			(void*)0                          // array buffer offset
			);

		// Draw the triangle !
		
		glDrawArrays(GL_TRIANGLES, 0, cur_mesh->vertex_size); // 12*3 indices starting at 0 -> 12 triangles

		glDisableVertexAttribArray(0);
		glDisableVertexAttribArray(1);
		//printText2D("N NinjaHead", 10, 50, 20);
		//printText2D("C Cube", 10, 80, 20);
        //glUseProgram(0);
        //DrawTexCube(32, 32, 32, cubetext);
		// Swap buffers
		glfwSwapBuffers(window);
		// Poll for and process events
		glfwPollEvents();
		
		glfwSetErrorCallback(error_callback);
		
	}// Check if the ESC key was pressed or the window was closed
	while (glfwGetKey(window,GLFW_KEY_ESCAPE) != GLFW_PRESS && 
		!glfwWindowShouldClose(window));

	// Cleanup VBO and shader
	glDeleteProgram(programID);
	glDeleteVertexArrays(1, &VertexArrayID);

	// Close OpenGL window and terminate GLFW
	glfwTerminate();

	return 0;
}

